package Avocado.form {

	import com.adobe.utils.StringUtil;
	import com.greensock.TweenLite;
	import com.greensock.TweenMax;
	
	import fl.transitions.Tween;
	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.globalization.StringTools;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	
	import mx.formatters.Formatter;

	public class FormField extends MovieClip {
		
		public var background:Sprite = new Sprite;
		public var lockMask:Sprite = new Sprite;
		
		private var fieldWidth:Number;
		private var textBase:String;
		private var maxChars:Number;
		private var bgColor:uint;
		private var bgOpacity:Number;
		private var rule:RegExp;
		private var replaceTo:*;
		private var errorColor:uint;
		private var errorOpacity:Number;
		private var errorAmount:Number;
		
		private var _lock:Boolean;
		private var _validateBase:Boolean = new Boolean(true);
		private var _justUpperCase:Boolean = new Boolean(false);
		private var _stringedMask:String;
		private var _landingMotion:Object;
		private var stringText:String;

		private var format:TextFormat;
		public var field:TextField = new TextField;
		
		
		/**
		 * FormField
		 * 
		 * creates easyly a FormField applicable, returns a movieclip
		 * 
		 * #TODO
		 * transition effects on focus_in / focus_out on a field
		 * 
		 * @_format : TextFormat - the text format of the field
		 * @_fieldWidth : Number - the number in pixels of the width of the field, default 30
		 * @_textBase : String - the base text of a field
		 * @_maxChars : Number - the maximu number of existing characthers on the field, default 256
		 * @_bgColor : uint - background color value
		 * @_bgOpacity : Number - the number between 0 and 1 to apply a background opacity
		 * @_rule : RegExp - the regular expression to apply on a field
		 * @_replaceTo : * - can be a method or string to filter the regular expression
		 * 
		 * ***/
		public function FormField(_format:TextFormat, _fieldWidth:Number=30, _textBase:String='', _maxChars:Number=256, _bgColor:uint=0xBDBEC1, _bgOpacity:Number=1, _rule:RegExp=null, _replaceTo:*=null, _errorColor:uint=0xee0000, _errorOpacity:Number=1, _errorAmount:Number=1) {
			super();

			this.format = _format;
			this.fieldWidth = _fieldWidth;
			this.textBase = _textBase;
			this.bgColor = _bgColor;
			this.bgOpacity = _bgOpacity;
			this.maxChars = _maxChars;
			this.rule = _rule;
			this.replaceTo = _replaceTo;
			this.errorColor = _errorColor;
			this.errorOpacity = _errorOpacity;
			this.errorAmount = _errorAmount;

			this.addEventListener(Event.ADDED_TO_STAGE, initThis);
		}
		
		
		/**
		 * set the character landing motion
		 * ***/
		public function get landingMotion():Object
		{
			return _landingMotion;
		}

		public function set landingMotion(value:Object):void
		{
			_landingMotion = value;
		}

		/**
		 * set the stringed special mask for substitute of the regex mask
		 * @_value : String
		 * ***/
		public function set stringedMask(_value:String):void{
			_stringedMask = _value;
		}

		public function get stringedMask():String{
			return _stringedMask;
		}


		/**
		 * setup textfield value and auto validate the text
		 * @_value : String 
		 * ***/
		public function set textValue(_value:String):void{
			field.text = _value;
			validate();
		}
		public function get textValue():String{
			validate();
			return field.text.toString();
		}
		
		
		
		/**
		 * setup textfield password combos
		 * @_value : Boolean
		 * ***/
		public function set password(_value:Boolean):void{
			field.displayAsPassword = _value;
		}
		public function get password():Boolean{
			return field.displayAsPassword;
		}
		
		
		
		/**
		 * setup textfield base validation
		 * @_value : Boolean
		 * ***/
		public function set validateBase(_value:Boolean):void{
			_validateBase = _value;
		}
		public function get validateBase():Boolean{
			return _validateBase;
		}

		/**
		 * setup textfield to just express uppercase characters
		 * @_value : Boolean
		 * ***/
		public function set justUpperCase(_value:Boolean):void{
			_justUpperCase = _value;
		}
		public function get justUpperCase():Boolean{
			return _justUpperCase;
		}
		
		
		
		
		
		
		
		
		
		/**
		 * lock the text filed
		 * ***/
		public function lock():void{
			_lock = true;

			field.selectable = false;
			field.type = TextFieldType.DYNAMIC;
			field.removeEventListener(MouseEvent.CLICK, clearField);
			field.removeEventListener(FocusEvent.FOCUS_IN, clearField);
			field.removeEventListener(FocusEvent.FOCUS_OUT, exitField);
			field.removeEventListener(KeyboardEvent.KEY_UP, refreshKey);
			field.removeEventListener(Event.CHANGE, checkUpperCase);
			
			field.setSelection(0,0);
			
			addChild(lockMask);
		}

		/**
		 * unlock the text filed
		 * ***/
		public function unlock():void{
			_lock = false;

			field.selectable = true;
			field.type = TextFieldType.INPUT;
			field.addEventListener(MouseEvent.CLICK, clearField);
			field.addEventListener(FocusEvent.FOCUS_IN, clearField);
			field.addEventListener(FocusEvent.FOCUS_OUT, exitField);
			field.addEventListener(KeyboardEvent.KEY_UP, refreshKey);
			field.addEventListener(Event.CHANGE, checkUpperCase);
			
			if ( (lockMask) && (lockMask.stage) ) {
				removeChild(lockMask);
			}
		}
		
		
		/**
		 * validate the field based on a regular expression
		 * ***/
		public function validate():Boolean{
			var valid:Boolean = new Boolean(false);
			if (rule) {
				if (field.text.toString().search(rule) < 0) {
					TweenMax.to(background, .33, {alpha: errorOpacity, colorTransform:{tint:errorColor, tintAmount: errorAmount}});
					valid = false;
				} else {
					if (replaceTo){
						field.text = field.text.toString().replace(rule,replaceTo);
					}
					TweenMax.to(background, .33, {alpha: bgOpacity, colorTransform:{tint:errorColor, tintAmount: 0}});
					valid = true;
				}

			} else {
				valid = true;
			}

			if (_validateBase == true) {
				if (field.text == this.textBase){
					valid = false;
				} else {
					valid = true;
				}
			}

			if (valid == true) {
				TweenMax.to(background, .33, {alpha: bgOpacity, colorTransform:{tint: errorColor, tintAmount: 0}});
			} else {
				TweenMax.to(background, .33, {alpha: errorOpacity, colorTransform:{tint: errorColor, tintAmount: errorAmount}});
			}
			
			return valid;
		}
		
		
		/**
		 * reset the text field to this default value
		 * ***/
		public function reset():void{
			field.text = this.textBase;
			this.stage.focus = this.stage;
		}


		/**
		 * change the text filed string characters to uppercase only
		 * ***/
		public function convertToUppercase(locale:Boolean=false):void{
			if (locale == false) {
				field.text = field.text.toUpperCase();
			} else {
				field.text = field.text.toLocaleUpperCase();	
			}
		}


		/**
		 * change the text filed string characters to uppercase only
		 * ***/
		public function convertToLowercase(locale:Boolean=false):void{
			if (locale == false) {
				field.text = field.text.toLowerCase();
			} else {
				field.text = field.text.toLocaleLowerCase();	
			}
		}
	
		
		
		
		
		/**
		 * 
		 * ***/
		private function initThis(e:Event):void{
			this.removeEventListener(Event.ADDED_TO_STAGE, initThis);
			
			setupBackgorund();
			setupField();
			
			this.addEventListener(Event.REMOVED_FROM_STAGE, removeThis);
		}
		
		/**
		 * 
		 * ***/
		private function removeThis(e:Event):void{
			this.removeEventListener(Event.REMOVED_FROM_STAGE, removeThis);
			
			field.removeEventListener(MouseEvent.CLICK, clearField);
			field.removeEventListener(FocusEvent.FOCUS_IN, clearField);
			field.removeEventListener(FocusEvent.FOCUS_OUT, exitField);
			field.removeEventListener(KeyboardEvent.KEY_UP, refreshKey);
			field.removeEventListener(Event.CHANGE, checkUpperCase);
		}


		/**
		 * setups the text background and the clickMask
		 * ***/
		private function setupBackgorund():void{
			background = new Sprite;
			background.graphics.beginFill(this.bgColor);
			background.graphics.drawRoundRect(0, 0, fieldWidth, 1, 4, 4);
			background.graphics.endFill();
			background.alpha = this.bgOpacity;
			addChild(background);
			
			lockMask = new Sprite;
			lockMask.graphics.beginFill(0x000000);
			lockMask.graphics.drawRect(0, 0, fieldWidth, 1);
			lockMask.graphics.endFill();
			lockMask.alpha = 0;
		}
			private function updateBackground():void{
				background.graphics.clear();
				background.graphics.beginFill(this.bgColor);
				background.graphics.drawRoundRect(0, 0, fieldWidth, field.textHeight+6, 4, 4);
				background.graphics.endFill();
				background.alpha = this.bgOpacity;
				
				lockMask.graphics.clear();
				lockMask.graphics.beginFill(0x000000);
				lockMask.graphics.drawRect(0, 0, fieldWidth, field.textHeight+6);
				lockMask.graphics.endFill();
				lockMask.alpha = 0;
			}
		
		
		/**
		 * setups the text field
		 * ***/
		private function setupField():void{
			field.text = textBase;
			field.embedFonts = true;
			field.defaultTextFormat = format;
			field.setTextFormat(format);
			field.mouseWheelEnabled = false;
			field.useRichTextClipboard = false;
			field.type = TextFieldType.INPUT;
			field.width = fieldWidth - 6;
			field.height = format.size + 6;
			field.multiline = false;
			field.x = 3;
			field.y = 3;
			field.maxChars = this.maxChars;
			field.addEventListener(MouseEvent.CLICK, clearField);
			field.addEventListener(FocusEvent.FOCUS_IN, clearField);
			field.addEventListener(FocusEvent.FOCUS_OUT, exitField);
			field.addEventListener(KeyboardEvent.KEY_UP, refreshKey);
			field.addEventListener(KeyboardEvent.KEY_DOWN, refreshKey);
			field.addEventListener(Event.CHANGE, checkUpperCase);
			addChild(field);
			
			updateBackground();
			
			if (_lock == true)
				this.lock();
		}
		
		/**
		 * clear the text filed
		 * ***/
		public function clearField(e:Event=null):void{
			if (field.text == textBase){
				field.text = '';
			} else {
				field.setSelection(0, field.maxChars);	
			}
			TweenMax.to(background, .33, {alpha: bgOpacity, colorTransform:{tint:errorColor, tintAmount:0}});
		}
		
		/**
		 * run when the user leave the field
		 * ***/
		private function exitField(e:Event=null):void{
			this.validate();
			if (field.text == ''){
				field.text = textBase;
			}
			TweenMax.to(background, .33, {alpha: bgOpacity, colorTransform:{tint:errorColor, tintAmount:0}});
		}
		
		/**
		 * run when the user press any key
		 * ***/
		private function refreshKey(e:KeyboardEvent=null):void{
			if (e.keyCode == 13) {
				this.validate();
				this.stage.focus = this.stage;
			} else if (e.keyCode == 8) {
				// null code to skip character
				// trace(e.charCode);
			} else {
				if (stringedMask) {
					var stringedMaskArray:Array = stringedMask.split('');
					var textArray:Array = field.text.split('');
					
					for (var s:* in stringedMaskArray) {
						if (stringedMaskArray[s] != '_') {
							if (textArray.length == s) {
								textArray.push(stringedMaskArray[s]);
							}
						}
					}
					
					field.text = textArray.join('');
					field.setSelection(field.text.length,field.text.length);
				} else {
					if (e.type == 'keyUp') {
						if (landingMotion) {
							moveCharacterLanding(String.fromCharCode(e.charCode));							
						}
					}
				}
			}
		}
		
		/**
		 * run when the user press enter
		 * ***/
		private function checkUpperCase(e:Event):void{
			if (justUpperCase == true) {
				convertToUppercase();
			}
		}
		
		
		
		private function moveCharacterLanding(_char:String):void{
			var mText:TextField = new TextField;
			mText.embedFonts = true;
			mText.defaultTextFormat = format;
			mText.setTextFormat(format);
			mText.mouseWheelEnabled = false;
			mText.useRichTextClipboard = false;
			mText.x = field.x + field.textWidth - 17 - field.scrollH;
			mText.y = field.y;
			mText.text = _char;
			addChild(mText);
			
			var resetPosition:Object = new Object;
			resetPosition.x = field.x + field.textWidth - 17 - field.scrollH;
			resetPosition.y = field.y;
			resetPosition.scaleX = 1;
			resetPosition.scaleY = 1;
			resetPosition.autoAlpha = 1;
			resetPosition.rotation = 0;
			resetPosition.onComplete = moveCharacterLandingComplete;
			resetPosition.onCompleteParams = [mText];

			TweenMax.fromTo(mText, .3, landingMotion, resetPosition); 
		}
		
		private function moveCharacterLandingComplete(_mField:TextField):void {
			removeChild(_mField);
			_mField = null;
		}
		

	}
}